iT邦幫忙

2024 iThome 鐵人賽

DAY 14
0

Current Sprint: 2. 實作遊戲開始
repo: https://github.com/side-project-at-SPT/ithome-ironman-2024-san-juan
swagger docs: https://side-project-at-spt.github.io/ithome-ironman-2024-san-juan/

前情提要

  • 定義打出的建築:
    • id: 哪一種建築
    • good_id: 哪一張卡作為貨物
    • card_ids: 哪些卡放在建築下面
  • 定義玩家:
    • id: User id
    • hand: 手牌
    • buildings: 打出的建築
  • 遊戲開始階段,發給每位玩家一張染坊卡片作為起始建築

TODO

  1. 產生並儲存 random seed
  2. 洗勻價格卡
  3. 設置牌庫:抽出玩家人數的染坊卡片,洗勻剩餘卡牌成為牌庫(抽牌堆)
  4. 發給每位玩家一張染坊卡片,作為起始建築
  5. 玩家依序從牌庫抽取四張卡片,作為手牌
  6. 決定起始玩家

規則書有個變體是:每位玩家抽取 (4 + n(順位)) 張卡片,並棄至剩 4 張,作為起始手牌
因此,我們將 決定起始玩家 的時機調整到發牌前,方便變體實作

deal

ref: #183 聖胡安 SAN JUAN 桌遊教學影片|勃根地桌遊

決定起始玩家

定義 choose_starting_player 實體方法

  1. sort player by their user id
  2. set seed by game seed
  3. shuffle players, first player is starting_player
# app/models/game.rb

class Game < ApplicationRecord
# ...
    def start_new_game(seed: nil)
      game = new(status: :playing)

      # 1. generate a random seed
      game.seed = seed || SecureRandom.hex(16)
      game.save

      # 1.1. generate players
      game.game_data[:players] = generate_players(seed: game.seed).to_json
      game.game_data[:current_player_index] = 0
      game.save
      
      # ...
    end
  
    def generate_players(seed: nil)
      srand(seed.to_i(16)) if seed

      human_player = Player.new(1, [], [])
      bot_players = 3.times.map { |i| Player.new(i + 2, [], []) }

      ([ human_player ] + bot_players).shuffle
    end
# ...
end

發牌給玩家

每位玩家發 4 張牌

# app/models/game.rb

class Game < ApplicationRecord
# ...
    def start_new_game(seed: nil)
      # ...

      # 3.3. shuffle the remaining cards to form a supply pile
      deck.shuffle!

      # 4. Give each player 1 indigo plant as their initial building
      players = game.players

      players.each do |player|
        player.buildings += [ Building.new("01") ]
      end

      # 5. deal 4 cards to each player as their initial hand, hidden from other players
      players.each do |player|
        player.hand = deck.shift(4)
      end

      # save the game data
      game.game_data[:players] = players.to_json
      game.game_data[:supply_pile] = deck
      game.save
      
      # ...
    end
# ...
end

更新 game_spec.rb

  • 第一位玩家 id 是否為 1
  • 第一位玩家手牌是否為 ["0", "0", "0", "0"]
  • 牌庫張數是否為 110 - 4 - 4 * 4 = 90
# spec/requests/api/v1/games_spec.rb

# ...

      response '200', 'Game created' do
        schema type: :object,
          properties: {
            id: { type: :integer },
            status: { type: :string }
          },
          required: [ 'id', 'status' ]

        run_test! do
          json = JSON.parse(response.body)
          expect(json['status']).to eq('playing')
          expect(json['game_config']['seed']).to eq('1234567890abcdef')
          expect(json['game_data']['current_price']).to match_array([ 1, 2, 2, 2, 3 ])
          expect(json['game_data']['supply_pile'].size).to eq(110 - 4 - 4 * 4)
          # pp json['game_data']['supply_pile'].index { |card| card == '01' }
          expect(json['game_data']['supply_pile'][27]).to eq("01")
          expect(json['game_data']['players'].size).to eq(4)
          expect(json['game_data']['players'][0]['buildings'].size).to eq(1)
          expect(json['game_data']['players'][0]['buildings'][0]['id']).to eq("01")
          # pp json['game_data']['players'].map { |player| player['id'] }
          expect(json['game_data']['current_player_index']).to eq(0)
          expect(json['game_data']['players'][0]['id']).to eq(1)
          expect(json['game_data']['players'][0]['hand'].size).to eq(4)
          expect(json['game_data']['players'][0]['hand']).to match_array([ "00", "00", "00", "00" ])
        end
      end

# ...

更新 _game.json.jbuilder

# app/views/api/v1/games/_game.json.jbuilder

# ...

  json.game_data do
    # ...

    json.current_player_index game.game_data["current_player_index"]
    
    # ...
  end

# ...

跑測試

rails s

# open another session

curl localhost:3000/api/v1/games -d '' | jq

{
  "id": 5,
  "status": "playing",
  "game_config": {
    "seed": "b860708c2dc20e1541a03054ee435951"
  },
  "game_data": {
    "current_price": [
      1,
      1,
      1,
      2,
      2
    ],
    "supply_pile": [], // 略
    "current_player_index": 0,
    "players": [
      {
        "id": 1,
        "hand": [
          "00",
          "00",
          "00",
          "00"
        ],
        "buildings": [
          {
            "id": "01"
          }
        ]
      },
      {
        "id": 2,
        "hand": [
          "01",
          "00",
          "00",
          "00"
        ],
        "buildings": [
          {
            "id": "01"
          }
        ]
      },
      {
        "id": 4,
        "hand": [
          "00",
          "00",
          "00",
          "00"
        ],
        "buildings": [
          {
            "id": "01"
          }
        ]
      },
      {
        "id": 3,
        "hand": [
          "00",
          "00",
          "01",
          "00"
        ],
        "buildings": [
          {
            "id": "01"
          }
        ]
      }
    ]
  }
}

收工.

  1. 產生並儲存 random seed
  2. 決定起始玩家
  3. 洗勻價格卡
  4. 抽出玩家人數的染坊卡片,洗勻剩餘卡牌成為牌庫(抽牌堆)
  5. 發給每位玩家一張染坊卡片,作為起始建築
  6. 玩家依序從牌庫抽取四張卡片,作為手牌

小結

  • 今天的 diff 95634f6
  • 完成 Sprint 2: 實作遊戲開始
    • 決定起始玩家
    • 發給玩家手牌

明天要做什麼

還沒完成的項目有:

  • 回合轉換
    • 檢查手牌上限
    • 擁有 辦公大樓、銀行、教堂 的玩家執行行動
  • 玩家行動
    • 先提供行動 api 呼叫
      • POST
      • req params
        • phase: 礦工 | 議員 | 建造 | 生產 | 交易 | 回合開始
        • keeps=?
        • build=?
        • pay=?
        • produces=?
        • sells=?
        • special_target=?
        • discards=?
        • fills=?
    • 有時間再提供 可執行的行動 action candidates api 呼叫
    • 礦工階段
    • 議員階段
    • 建造階段
    • 生產階段
    • 交易階段
  • 行動玩家轉換
    • 玩家行動後,順時鐘方向換下一位玩家行動
    • 階段玩家都行動後,下一位玩家選擇職業
    • 所有玩家選擇職業後,開始下一回合
  • 遊戲結束計分
  • 城市卡片功能 (24 x 3 + 4 kind of 6-costs x 2)
    • 實作
      • 玩家建造城市卡後,將卡片能力登記至玩家能力上
      • 玩家行動時,將玩家能力加入行動規則判斷

預估每一種回合階段花 2-3 天、行動權轉換 1-2 天、計分 1 天
=> 3 * 5 + 2 + 1 = 18

不含其它的卡片實作 /images/emoticon/emoticon01.gif

以上不代表明天會做,如有雷同純屬巧合


工商服務

SPT (Side Project Taiwan) 的宗旨是藉由Side Project開發來成就自我,透過持續學習和合作,共同推動技術和專業的發展。我們相信每一個參與者,無論是什麼專業,都能在這個社群中找到屬於自己的成長空間。

歡迎所有對Side Project開發有興趣的人加入我們,可以是有點子來找夥伴,也可以是來尋找有興趣的Side Project加入,邀請大家一同打造一個充滿活力且有意義的技術社群!

Discord頻道連結:https://sideproj.tw/dc


上一篇
Day 13 - S2: 發給玩家染坊卡片作為起始建築
下一篇
Day 15 - 回顧遊戲流程
系列文
透過實作網頁遊戲練習網站工程師的基本素養,以 San Juan(聖胡安) 為例。30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言